//#include <linux/kernel.h>
//#include <linux/module.h>
//#include <linux/init.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/jhash.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/slab.h>
#include <linux/version.h>

#include "socs/mod/addr_opt.h"
#include "socs/mod/neigh.h"
#include "socs/mod/hello.h"
#include "socs/comm/config.h"
#include "socs/comm/socs.h"
#include "socs/comm/types.h"
#include "socs/mod/multicast.h"

#include "socs/mod/lladdr_resolve.h"

#if LINUX_VERSION_CODE == KERNEL_VERSION(3, 12, 0)

#define list_last_entry(ptr, type, member) \
        list_entry((ptr)->prev, type, member)

#define list_next_entry(pos, member) \
        list_entry((pos)->member.next, typeof(*(pos)), member)

#endif
//MODULE_LICENSE("GPL");

static LIST_HEAD(socs_neigh4);
//static u64 socs_neigh4_lclcount;

static LIST_HEAD(socs_neigh6);
//static u64 socs_neigh6_lclcount;
static DEFINE_RWLOCK(socs_neigh_lock4);
static DEFINE_RWLOCK(socs_neigh_lock6);
static rwlock_t *socs_neigh_lock[FAMILY_NUM];
#define NEIGH_READ_LOCK(family) read_lock_bh(socs_neigh_lock[family])
#define NEIGH_READ_UNLOCK(family) read_unlock_bh(socs_neigh_lock[family])
#define NEIGH_WRITE_LOCK(family) write_lock_bh(socs_neigh_lock[family])
#define NEIGH_WRITE_UNLOCK(family) write_unlock_bh(socs_neigh_lock[family])

void socs_neigh_read_lock(int family)
{
	NEIGH_READ_LOCK(family);
}

void socs_neigh_read_unlock(int family)
{
	NEIGH_READ_UNLOCK(family);
}

extern unsigned int sendtimes;
static int __socs_neigh_update(struct localnode *lcn);
static int __socs_neigh_add(struct localnode *lcn, int family,
							int is_localnode);
static struct socs_neigh_node *__socs_neigh_get_localnode(int family);
static bool __is_in_socs_neigh(const struct localnode *lcn)
{
	struct socs_neigh_node *node;
	bool ret = false;

	if (list_empty(socs_neigh_get(lcn->family))) {
		log_debug("socs_neigh %d is empty\n", lcn->family);
		ret = false;
		goto out;
	}

	list_for_each_entry(node, socs_neigh_get(lcn->family), list_hook) {
		if (!memcmp(node->lcn.hostname, lcn->hostname, HOSTNAMESIZE)) {
			ret = true;
			goto out;
		}
	}
out:
	return ret;
}
/*it need both src and dest are change then it can be change the jhash2 */

unsigned int socs_neigh_hash(void  *src, void *dest, unsigned int totallink,
							 int family)
{
	unsigned int a, b;
	int len;

	if (family == IPV4) {
		len = sizeof(struct in_addr);
	} else {
		len = sizeof(struct in6_addr);
	}

	a = jhash(src, len, INITVAL_32);
	b = jhash(dest, len, INITVAL_32);
	return jhash_2words(a, b, INITVAL_32) % totallink;
}

int socs_neigh_find_by_addr(void *src, void *dest, int family, int inner_family,
							int *is_localnode, socs_addr_t *dIP)
{
	struct socs_neigh_node *node = NULL;
	struct locallink *lcl = NULL;
	unsigned int hash;
	struct list_head *head = socs_neigh_get(inner_family);
	unsigned int totallink = 0;
	int ret = -1;
	char hostname[HOSTNAMESIZE] = {0};
	int is_nat64 = 0;

	NEIGH_READ_LOCK(inner_family);

	if (list_empty(head)) {
		log_debug("The  socs_neigh is empty.");
		goto out;
	}

	list_for_each_entry(node, head, list_hook) {
		totallink += node->lcn.lcl_num;
	}
	hash = socs_neigh_hash(src, dest, totallink, inner_family);
	log_debug("socs_neigh_find_by_addr hash code=%u,totallink=%u", hash, totallink);
	list_for_each_entry(node, head, list_hook) {
		if (hash < node->lcn.lcl_num) {
			lcl = &node->lcn.lcls[hash];
			*is_localnode = node->is_localnode;
			goto out;
		} else if (hash == node->lcn.lcl_num) {
			node = list_entry(node->list_hook.next, struct socs_neigh_node, list_hook);
			lcl = &node->lcn.lcls[0];
			*is_localnode = node->is_localnode;
			goto out;
		} else {
			hash = hash - node->lcn.lcl_num;
		}
	}
out:

	if (lcl) {
		ret = 0;
		atomic_inc(&lcl->totalrq);

		if (family == IPV4) {
			dIP->ipv4 = lcl->address.ipv4;
		} else {
			if (inner_family == IPV4) {
				is_nat64 = 1;
				memcpy(hostname, node->lcn.hostname, strlen(node->lcn.hostname));  // NAT64,根据hostname查找node
			} else {
				dIP->ipv6 = lcl->address.ipv6;
			}
		}
	}
	NEIGH_READ_UNLOCK(inner_family);

	if (is_nat64) {
		ret = socs_neigh_find_by_hostname(hostname, family, dIP);
	}

	return ret;
}

int socs_neigh_find_by_hostname(const char *hostname, int family, socs_addr_t *dIP)
{
	struct socs_neigh_node *node = NULL;
	struct locallink *lcl = NULL;
	int  ret = -1;
	struct list_head *head = socs_neigh_get(family);

	NEIGH_READ_LOCK(family);
	list_for_each_entry(node, head, list_hook) {
		if (memcmp(node->lcn.hostname, hostname, strlen(node->lcn.hostname)) == 0) {
			if (node->lcn.lcl_num > 0) {
				lcl = &node->lcn.lcls[0];
				ret = 0;
				if (family == IPV4) {
					dIP->ipv4 = lcl->address.ipv4;
				} else {
					dIP->ipv6 = lcl->address.ipv6;
				}
			}
			break;
		}
	}
	NEIGH_READ_UNLOCK(family);
	return ret;
}

/*********************************************************************************************/
int socs_neigh_localnode_down(const char *ifname)
{
	struct socs_neigh_node *node4;
	struct socs_neigh_node *node6;
	struct localnode *lcn4;
	struct localnode *lcn6;
	int i, ret = -1;
	log_debug("socs_neigh_localnode_down");
	NEIGH_WRITE_LOCK(IPV4);
	NEIGH_WRITE_LOCK(IPV6);
	node4 = __socs_neigh_get_localnode(IPV4);
	node6 = __socs_neigh_get_localnode(IPV6);

	if (node6) {
		lcn6 = &node6->lcn;
		node_for_each_link(i, lcn6) {
			if (!memcmp(ifname, lcn6->lcls[i].name, IFNAMSIZ)) {
				log_debug("lcn6->lcls[%d],name:%s is down", i, lcn6->lcls[i].name);
				locallink_expire_now(&lcn6->lcls[i]);
			}
		}
		ret = 0;
	}

	if (node4) {
		lcn4 = &node4->lcn;
		node_for_each_link(i, lcn4) {
			if (!memcmp(ifname, lcn4->lcls[i].name, IFNAMSIZ)) {
				log_debug("lcn4->lcls[%d],name:%s is down", i, lcn4->lcls[i].name);
				locallink_expire_now(&lcn4->lcls[i]);
			}
		}
		ret = 0;
	}

	NEIGH_WRITE_UNLOCK(IPV6);
	NEIGH_WRITE_UNLOCK(IPV4);
	return ret;
}


/**************************************************
delete a address of one net_dev
args:
family : ipaddress type ,IPV4 or IPV6
**************************************************/
int socs_neigh_localnode_ip_down(const char *ifname, void *address, int family)
{
	struct socs_neigh_node *node;
	struct localnode *lcn;
	int i, ret = -1;
	log_debug("socs_neigh_localnode_ip_down");
	NEIGH_WRITE_LOCK(family);

	if (family == IPV4) {
		node = __socs_neigh_get_localnode(IPV4);

		if (node) {
			lcn = &node->lcn;
			node_for_each_link(i, lcn) {
				if (!memcmp(ifname, lcn->lcls[i].name, IFNAMSIZ)
					&& !addr_cmp(family, &lcn->lcls[i].address, address)) {
					log_debug("lcn4->lcls[%d],name:%s is down,inet addr:%pI4c", i,
							  lcn->lcls[i].name, address);
					locallink_expire_now(&lcn->lcls[i]);
				}
			}
			ret = 0;
		} else {
			log_debug("[socs_neigh_localnode_ip_down]the localnode4 is no in neigh list");
			ret = -1;
		}
	} else {
		node = __socs_neigh_get_localnode(IPV6);

		if (node) {
			lcn = &node->lcn;
			node_for_each_link(i, lcn) {
				if (!memcmp(ifname, lcn->lcls[i].name, IFNAMSIZ)
					&& !addr_cmp(family, &lcn->lcls[i].address, address)) {
					log_debug("lcn6->lcls[%d],name:%s is down,inet addr:%pI6c", i,
							  lcn->lcls[i].name, address);
					locallink_expire_now(&lcn->lcls[i]);
				}
			}
			ret = 0;
		} else {
			log_debug("[socs_neigh_localnode_ip_down]the localnode6 is no in neigh list");
			ret = -1;
		}
	}

	NEIGH_WRITE_UNLOCK(family);
	return ret;
}



int socs_neigh_localnode_up(struct net_device *dev)
{
	struct localnode *lcn4;
	struct localnode *lcn6;
	struct socs_neigh_node *node4;
	struct socs_neigh_node *node6;
	int err;
	char hostname[HOSTNAMESIZE] = {};
	socs_config_get(ARGP_HOSTNAME, hostname);
	log_debug("socs_neigh_localnode_up");
	NEIGH_WRITE_LOCK(IPV4);
	NEIGH_WRITE_LOCK(IPV6);
	node4 = __socs_neigh_get_localnode(IPV4);
	node6 = __socs_neigh_get_localnode(IPV6);

	if (!node4) {
		lcn4 = (struct localnode *)kmalloc(SIZEOFLCN + SIZEOFLCL * LOCALLINKSIZE,
										   GFP_ATOMIC);
		log_debug("localnode4 is null,so need to creat a socs_neigh node");
		memset(lcn4, 0, SIZEOFLCN + SIZEOFLCL * LOCALLINKSIZE);
		memcpy(lcn4->hostname, hostname, HOSTNAMESIZE);
		lcn4->family = IPV4;
		lcn4->totalsapcnt = 0;
		lcn4->lcl_num = 0;
		err = localnode4_get(dev, lcn4);

		if (err < 0) {
			log_debug("localnode4_get ipv4  failed");
		}

		if (lcn4->lcl_num) {
			__socs_neigh_add(lcn4, IPV4, 1);
		}

		kfree(lcn4);
	} else {
		log_debug("localnode4 is not null,just need to add to the localnode4");
		err = localnode4_get(dev, &node4->lcn);

		if (err < 0) {
			log_debug("localnode4_get ipv4  failed");
		}
	}

	if (!node6) {
		log_debug("localnode6 is null,so need to creat a socs_neigh node");
		lcn6 = (struct localnode *)kmalloc(SIZEOFLCN + SIZEOFLCL * LOCALLINKSIZE,
										   GFP_ATOMIC);
		memset(lcn6, 0, SIZEOFLCN + SIZEOFLCL * LOCALLINKSIZE);
		memcpy(lcn6->hostname, hostname, HOSTNAMESIZE);
		lcn6->family = IPV6;
		lcn6->totalsapcnt = 0;
		lcn6->lcl_num = 0;
		err = localnode6_get(dev, lcn6);

		if (err < 0) {
			log_debug("localnode6_get ipv6  failed");
		}

		if (lcn6->lcl_num) {
			__socs_neigh_add(lcn6, IPV6, 1);
		}

		kfree(lcn6);
	} else {
		log_debug("localnode6 is not null,just need to add to the localnode6");
		err = localnode6_get(dev, &node6->lcn);

		if (err < 0) {
			log_debug("localnode6_get ipv6  failed");
		}
	}

	NEIGH_WRITE_UNLOCK(IPV6);
	NEIGH_WRITE_UNLOCK(IPV4);
	socs_neigh_send_localnode();
	sendtimes = 0;
	return 0;
}
static struct socs_neigh_node *__socs_neigh_get_localnode(int family)
{
	struct socs_neigh_node *node = NULL;

	if (list_empty(socs_neigh_get(family))) {
		return NULL;
	}

	list_for_each_entry(node, socs_neigh_get(family), list_hook) {
		if (node->is_localnode) {
			break;
		}
	}

	if ((&node->list_hook != socs_neigh_get(family)) && node->is_localnode) {
		return node;
	}

	return NULL;
}
int socs_neigh_send_localnode(void)
{
	struct socs_neigh_node *node4;
	struct socs_neigh_node *node6;
	int ret;
	NEIGH_READ_LOCK(IPV4);
	NEIGH_READ_LOCK(IPV6);
	node4 = __socs_neigh_get_localnode(IPV4);
	node6 = __socs_neigh_get_localnode(IPV6);

	if (!node4
		&& !node6) { /*both node4 and node6 are NULL ,this node is off-line,need to clear socs_neigh and sap vote info*/
		NEIGH_READ_UNLOCK(IPV6);
		NEIGH_READ_UNLOCK(IPV4);
		log_info("this node is off-line ,just clear socs_neigh and sap_vote info");
		socs_neigh_destroy();
		saptb_flush(IPV4);
		saptb_flush(IPV6);
		/* socs_neigh_show(IPV4); */
		/* socs_neigh_show(IPV6); */
		/* saptb_show(saptb_get(IPV4), 0); */
		/* saptb_show(saptb_get(IPV6), 0); */
		return send_hello_basic(NULL, NULL);
	} else {
		ret = send_hello_basic(node4 ? &node4->lcn : NULL,
							   node6 ? &node6->lcn : NULL);
		NEIGH_READ_UNLOCK(IPV6);
		NEIGH_READ_UNLOCK(IPV4);
		return ret;
	}
}
static int  socs_neigh_recv_basic_handler(struct localnode *lcn)
{
	int ret;

	if (!lcn) {
		log_err(ERR_NULL_PTR, "%s lcn is null", __func__); /*it is normal*/
		return 0;
	}

	NEIGH_WRITE_LOCK(lcn->family);

	if (__is_in_socs_neigh(lcn)) {
		ret = __socs_neigh_update(lcn);
	} else {
		ret = __socs_neigh_add(lcn, lcn->family, 0);
	}

	NEIGH_WRITE_UNLOCK(lcn->family);
	return ret;
}
int socs_neigh_recv_basic(struct hello_packet *hello)
{
	struct localnode tmp;
	int len4;

	if (hello->opt != HELLO_BASIC || hello->vote_type != NOVOTE) {
		return -1;
	}

	memcpy(&tmp, hello->data, SIZEOFLCN);

	switch (tmp.family) {
	case IPV4:/*maybe the lcn6 is available*/
		{
			len4 = SIZEOFLCN + SIZEOFLCL * (tmp.lcl_num);
			if (hello->data_len == len4) { /*only lcn4 is available*/
				socs_neigh_recv_basic_handler((struct localnode *)hello->data);
			} else {
				socs_neigh_recv_basic_handler((struct localnode *)hello->data);
				socs_neigh_recv_basic_handler((struct localnode *)(hello->data + len4));
			}
		}
		break;

	case IPV6:/*only the lcn6 is available*/
		socs_neigh_recv_basic_handler((struct localnode *)hello->data);
		break;

	default:
		log_err(ERR_UNKNOW_FAMILY, "%s unknow lcn->family %d,do nothing", __func__,
				tmp.family);
		return -1;
	}

	return 0;
}
/****************************************************************************************************************/
struct list_head *socs_neigh_get(int family)
{
	if (family == IPV4) {
		return &socs_neigh4;
	} else {
		return &socs_neigh6;
	}
}

static int __socs_neigh_add(struct localnode *lcn, int family, int is_localnode)
{
	struct socs_neigh_node *node_added, *node = NULL;
	struct list_head *pos;
	int i;
	log_debug("socs_neigh_add");

	if (!lcn) {
		log_err(ERR_NULL_PTR, "%s NULL is not a valid socs_neigh.", __func__);
		return -1;
	}

	node_added = kmalloc(sizeof(struct socs_neigh_node) + SIZEOFLCL * LOCALLINKSIZE,
						 GFP_ATOMIC);

	if (!node_added) {
		log_err(ERR_ALLOC_FAILED, "%s Allocation of  socs_neigh node failed.",
				__func__);
		return -1;
	}

	memset(node_added, 0, sizeof(struct socs_neigh_node) + SIZEOFLCL *
		   LOCALLINKSIZE);
	node_for_each_link(i, lcn) {
		atomic_set(&lcn->lcls[i].totalrq, 0);
	}
	memcpy(&node_added->lcn, lcn, SIZEOFLCN + SIZEOFLCL * (lcn->lcl_num));
	node_added->is_localnode = is_localnode;

	localnode_settimer(&node_added->lcn, is_localnode);
	list_for_each(pos, socs_neigh_get(family)) {
		node = list_entry(pos, struct socs_neigh_node, list_hook);

		if (memcmp(node->lcn.hostname, node_added->lcn.hostname, HOSTNAMESIZE) > 0) {
			break;
		}
	}

	if (pos == socs_neigh_get(family)) {
		list_add_tail(&node_added->list_hook, socs_neigh_get(family));
	} else {
		list_add(&node_added->list_hook, pos->prev);
	}

	return 0;
}

static int __socs_neigh_update(struct localnode *lcn)
{
	struct socs_neigh_node *node;
	int add_flag = 0;

	if (list_empty(socs_neigh_get(lcn->family))) {
		log_debug("socs_neigh %d is empty", lcn->family);
		return -1;
	}

	list_for_each_entry(node, socs_neigh_get(lcn->family), list_hook) {
		if (!memcmp(node->lcn.hostname, lcn->hostname, HOSTNAMESIZE)) {
			add_flag = localnode_update(&node->lcn, lcn);
			return 0;
		}
	}
	return -1;
}
int socs_neigh_getlcl_unequal(int family, const char *ifname,
							  struct locallink *lcl)
{
	int i, ret = FAMILY_INVALID;
	struct net_device *dev = NULL;
	struct socs_neigh_node *node;
	log_debug("socs_neigh_getlcl_unequal");
	NEIGH_READ_LOCK(family);
	node = __socs_neigh_get_localnode(family);

	if (node) {
		node_for_each_link(i, &node->lcn) {
			if (ifname && memcmp(node->lcn.lcls[i].name, ifname, IFNAMSIZ) == 0) {
				continue;
			}

			dev = dev_get_by_name(&init_net, node->lcn.lcls[i].name);

			if (dev && !netif_running(dev)) {
				dev_put(dev);
				continue;
			}

			if (dev) {
				dev_put(dev);
			}

			memcpy(lcl, &node->lcn.lcls[i], SIZEOFLCL);
			log_debug("the lcl get is %s", lcl->name);
			ret = family;
			break;
		}
	}

	NEIGH_READ_UNLOCK(family);
	//end add by Leo.Huang 2015-03-20
	return ret;
}
/*
 *@ return IPV4 ,remove the address is in the socs_neigh4
  @ return IPV6 ,remove the address is in the socs_neigh6
 * */
int socs_neigh_remove(void *address) /**/
{
	struct socs_neigh_node *node;
	int res, ret = FAMILY_INVALID;
	NEIGH_WRITE_LOCK(IPV4);
	NEIGH_WRITE_LOCK(IPV6);

	if (list_empty(socs_neigh_get(IPV4))) {
		log_debug("socs_neigh %d is empty\n", IPV4);
	} else {
		list_for_each_entry(node, socs_neigh_get(IPV4), list_hook) {
			log_debug("[socs_neigh_remove]locallink_remove address %pI4c", address);
			res = locallink_remove(&node->lcn, address, node->is_localnode);

			if (!res) { /*find and remove*/
				if (!node->lcn.lcl_num) { /*lcn.lcl_num=0,need to del this socs_neigh_node*/
					//            		        socs_neigh4_lclcount--;
					list_del(&node->list_hook);
					kfree(node);
				}

				ret = IPV4;
				goto out;
			}
		}
	}

	if (list_empty(socs_neigh_get(IPV6))) {
		log_debug("socs_neigh %d is empty\n", IPV6);
		goto out;
	}

	list_for_each_entry(node, socs_neigh_get(IPV6), list_hook) {
		res = locallink_remove(&node->lcn, address, node->is_localnode);

		if (!res) {
			if (!node->lcn.lcl_num) {
				list_del(&node->list_hook);
				kfree(node);
			}

			ret = IPV6;
			goto out;
		}
	}
	log_debug("socs_neigh_remove,the address is already expire or not in the socs_neigh list");
out:
	NEIGH_WRITE_UNLOCK(IPV6);
	NEIGH_WRITE_UNLOCK(IPV4);
	return ret;
}
void socs_neigh_destroy(void)
{
	NEIGH_WRITE_LOCK(IPV4);

	while (!list_empty(&socs_neigh4)) {
		struct socs_neigh_node *node = container_of(socs_neigh4.next,
									   struct socs_neigh_node, list_hook);
		locallink_remove_all(&node->lcn);
		list_del(&node->list_hook);
		kfree(node);
	}

	NEIGH_WRITE_UNLOCK(IPV4);
	NEIGH_WRITE_LOCK(IPV6);

	while (!list_empty(&socs_neigh6)) {
		struct socs_neigh_node *node = container_of(socs_neigh6.next,
									   struct socs_neigh_node, list_hook);
		locallink_remove_all(&node->lcn);
		list_del(&node->list_hook);
		kfree(node);
	}

	NEIGH_WRITE_UNLOCK(IPV6);
}
void socs_neigh_show(int family)
{
	struct list_head *head = socs_neigh_get(family);
	struct socs_neigh_node *node;
	NEIGH_READ_LOCK(family);

	if (list_empty(head)) {
		log_debug( "The socs_neigh %d is empty", family);
	}

	list_for_each_entry(node, head, list_hook) {
		localnode_show(&node->lcn);
	}
	NEIGH_READ_UNLOCK(family);
}

/* static void xxx(struct sap *sap, void *p)                                             */
/* {                                                                                     */
/*     if (sap) {                                                                        */
/*         printk("MAYC %s: %pI4c %pI6c\n", __func__, &sap->saIP.ipv4, &sap->saIP.ipv6); */
/*     }                                                                                 */
/* }                                                                                     */

int socs_neigh_vote(struct sap_table *saptb)
{
	int j, ret = -1, index, min_sapcnt;
	struct list_head *head = socs_neigh_get(saptb->family);
	struct socs_neigh_node *node, *first, *last;
	struct sap *sap;
	struct rb_node *saptb_node;
	NEIGH_WRITE_LOCK(saptb->family);

	if (list_empty(head)) {
		goto out;
	}

	list_for_each_entry(node, head, list_hook) {
		node->lcn.totalsapcnt = 0;
		node_for_each_link(j, &node->lcn) {
			node->lcn.lcls[j].sapcnt = 0;
		}
	}
	node = first = list_first_entry(head, struct socs_neigh_node, list_hook);
	last = list_last_entry(head, struct socs_neigh_node, list_hook);
	saptb->locallinkcnt = 0;

	for (saptb_node = rb_first(&saptb->sap_tree); saptb_node;
		 saptb_node = rb_next(saptb_node)) {
		sap = rb_entry(saptb_node, struct sap, tree_hook);
		sap->status = SERVING;

		if (saptb->family == IPV6 && sap->prefix < 128) {
			sap->is_locallink = 1;
			continue;
		}

		index = -1;
		min_sapcnt = 0xffff;
		node_for_each_link(j, &node->lcn) {
			if (node->lcn.lcls[j].sapcnt < min_sapcnt) {
				index = j;
				min_sapcnt = node->lcn.lcls[j].sapcnt;
			}
		}

		if (saptb->family == IPV4) {
			memcpy(&sap->dIP.ipv4, &node->lcn.lcls[index].address.ipv4,
				   sizeof(struct in_addr));
		} else {
			memcpy(&sap->dIP.ipv6, &node->lcn.lcls[index].address.ipv6,
				   sizeof(struct in6_addr));
		}

		node->lcn.lcls[index].sapcnt++;
		node->lcn.totalsapcnt++;
		sap->is_locallink = node->is_localnode;

		if (sap->is_locallink) {
			saptb->locallinkcnt++;

			if (saptb->family == IPV4) {
				sap_send_gratuitous_arp(&sap->saIP.ipv4, node->lcn.lcls[index].name);
			} else {
				sap_send_unsol_na(&sap->saIP.ipv6, node->lcn.lcls[index].name);
			}
		}

		if (node == last) {
			node = first;
		} else {
			node = list_next_entry(node, list_hook);
		}
	}

	/* saptb_for_each_sap(i, saptb) {                                                              */
	/*     saptb->saps[i].status = SERVING;                                                        */
	/*     if (saptb->family == IPV6 && saptb->saps[i].prefix < 128) {                             */
	/*         saptb->saps[i].is_locallink = 1;                                                    */
	/*         continue;                                                                           */
	/*     }                                                                                       */
	/*     index = -1;                                                                             */
	/*     min_sapcnt = 0xffff;                                                                    */
	/*     node_for_each_link(j, &node->lcn) {                                                     */
	/*         if (node->lcn.lcls[j].sapcnt < min_sapcnt) {                                        */
	/*             index = j;                                                                      */
	/*             min_sapcnt = node->lcn.lcls[j].sapcnt;                                          */
	/*         }                                                                                   */
	/*     }                                                                                       */
	/*     if (saptb->family == IPV4) {                                                            */
	/*         memcpy(&saptb->saps[i].dIP.ipv4, &node->lcn.lcls[index].address.ipv4,               */
	/*                sizeof(struct in_addr));                                                     */
	/*     } else {                                                                                */
	/*         memcpy(&saptb->saps[i].dIP.ipv6, &node->lcn.lcls[index].address.ipv6,               */
	/*                sizeof(struct in6_addr));                                                    */
	/*     }                                                                                       */
	/*     node->lcn.lcls[index].sapcnt++;                                                         */
	/*     node->lcn.totalsapcnt++;                                                                */
	/*     saptb->saps[i].is_locallink = node->is_localnode;                                       */
	/*     if (saptb->saps[i].is_locallink) {                                                      */
	/*         saptb->locallinkcnt++;                                                              */
	/*         if (saptb->family == IPV4) {                                                        */
	/*             sap_send_gratuitous_arp(&saptb->saps[i].saIP.ipv4, node->lcn.lcls[index].name); */
	/*         } else {                                                                            */
	/*             sap_send_unsol_na(&saptb->saps[i].saIP.ipv6, node->lcn.lcls[index].name);       */
	/*         }                                                                                   */
	/*     }                                                                                       */
	/*     if (node == last) {                                                                     */
	/*         node = first;                                                                       */
	/*     } else {                                                                                */
	/*         node = list_next_entry(node, list_hook);                                            */
	/*     }                                                                                       */
	/* }                                                                                           */
	ret = 0;
out:
	NEIGH_WRITE_UNLOCK(saptb->family);
	return ret;
}

int  socs_neigh_init(void)
{
	struct localnode *lcn4 = NULL;
	struct localnode *lcn6 = NULL;
	int err;
	socs_neigh_lock[IPV4] = &socs_neigh_lock4;
	socs_neigh_lock[IPV6] = &socs_neigh_lock6;
	lcn4 = (struct localnode *)kmalloc(SIZEOFLCN + SIZEOFLCL * LOCALLINKSIZE,
									   GFP_ATOMIC);
	lcn6 = (struct localnode *)kmalloc(SIZEOFLCN + SIZEOFLCL * LOCALLINKSIZE,
									   GFP_ATOMIC);
	memset(lcn4, 0, SIZEOFLCN + SIZEOFLCL * LOCALLINKSIZE);
	memset(lcn6, 0, SIZEOFLCN + SIZEOFLCL * LOCALLINKSIZE);
	err = localnode_get(lcn4, IPV4);

	if (err < 0) {
		log_debug("localnode_get ipv4  failed");
		kfree(lcn4); //mem release(DONE)
		kfree(lcn6);
		return err;
	}

	NEIGH_WRITE_LOCK(IPV4);

	if (lcn4->lcl_num) {
		__socs_neigh_add(lcn4, IPV4, 1);
	}

	NEIGH_WRITE_UNLOCK(IPV4);
	err = localnode_get(lcn6, IPV6);

	if (err < 0) {
		log_debug("localnode_get ipv6  failed");
		kfree(lcn4); //mem release(DONE)
		kfree(lcn6);
		return err;
	}

	NEIGH_WRITE_LOCK(IPV6);

	if (lcn6->lcl_num) {
		__socs_neigh_add(lcn6, IPV6, 1);
	}

	NEIGH_WRITE_UNLOCK(IPV6);
	kfree(lcn4);
	kfree(lcn6);
	socs_neigh_show(IPV4);
	socs_neigh_show(IPV6);
	return 0;
}
